from model.model import Model, StatisticDSM
from fastapi import HTTPException
from package.connector.elastic_db import es_db


class ModelView(Model):

    IndexPrefix = 'dsm-'
    
    HOT_MAX_SIZE = '40gb'
    HOT_MAX_DOCS = 30000000
    SHARDS = 5
    REPLICAS = 0

    BuiltinField = {
        "_sourceid": {"type": "keyword"},  # 数据源
        "_parserid": {"type": "keyword"},  # 解析规则
        "_modelid": {"type": "keyword"},  # 模型ID

        "_time": {"type": "date", "format": "epoch_millis"},  # 日志发生时间
        "_host": {"type": "keyword"},  # 日志IP
        "_raw": {"type": "text"}  # 原始日志
    }

    @classmethod
    def _upsert_lifecycle(cls, name, cold_days=7, del_days=180):
        lifecycle = {
            "policy": {
                "phases": {
                    "hot": {
                        "actions": {
                            "rollover": {
                                "max_age": f'{cold_days}d',
                                "max_size": cls.HOT_MAX_SIZE,
                                "max_docs": cls.HOT_MAX_DOCS
                                },
                            "set_priority": {
                                "priority": 100
                                }
                            }
                        },
                    "warm": {
                        "actions": {
                            "allocate": {
                                "number_of_replicas": 0,
                                "require": {
                                    "temp": "*warm*"
                                    }
                                },
                                "shrink": {
                                    "number_of_shards": 1
                                    },
                                "forcemerge": {
                                    "max_num_segments": 1,
                                    "index_codec": 'best_compression'
                                    },
                                "set_priority": {
                                    "priority": 50
                                    }
                                }
                            },
                    'cold': {
                        "min_age": f"{cold_days}d",
                        "actions": {
                            "set_priority": {
                                "priority": 0
                                }
                            }
                        },
                    'delete': {
                        "min_age": f'{del_days}d',
                        "actions": {
                            "delete": {}
                            }
                        }
                    }
                }
            }
        resp = es_db.upsert_lifecycle(name=name, body=lifecycle)
        return resp

    @classmethod
    def _upsert_template(cls, name, mappings):
        template = {
            "priority": 100,
            "index_patterns": [f"{name}-*"],
            "template": {
                "settings": {
                    "index": {
                        "lifecycle.name": name,
                        "lifecycle.rollover_alias": name,
                        "number_of_replicas": cls.REPLICAS,
                        "number_of_shards": cls.SHARDS
                    }
                },
                "mappings": {'properties': {**mappings, **cls.BuiltinField}}
            },
            "composed_of": ['dsm_dft'],
            "_meta": {}
        }
        resp = es_db.upsert_template(name=name, body=template)
        return resp

    @classmethod
    def _create_index(cls, name, idx=1):
        index = {
            "aliases": {
                name: {
                    "is_write_index": True
                }
            }
        }
        resp = es_db.create_index(index=f'{name}-{idx:0>6}', body=index)
        return resp
    
    @classmethod
    def _create_template(cls, item):
        """创建模板"""
        resp = cls._upsert_lifecycle(item.index, cold_days=item.policy.get('cold_days', 7), del_days=item.policy.get('del_days', 180))
        resp = cls._upsert_template(item.index, item.mappings)
        resp = cls._create_index(item.index)
        return

    @classmethod
    def _rollover_index(cls, name):
        """rollover"""
        resp = es_db.rollover(index=name)
        return resp

    @classmethod
    def _delete_index(cls, name):
        """清除索引"""
        es_db.delete_index(index=f'*{name}*')
        es_db.delete_lifecycle(name=name)
        es_db.delete_template(name=name)
        return
    
    @classmethod
    def create_model(cls, session, body):
        """创建模型"""
        item = cls.create_item(session=session, body=body, commit=False)
        cls._create_template(item)
        session.commit()
        return item

    @classmethod
    def update_model(cls, session, filter_, update):
        """更新模型"""
        item = cls.get_item(session, filter_=filter_)
        if not item:
            raise HTTPException(status_code=404, detail='数据不存在')

        for k, v in update.items():
            if getattr(item, k) == v:
                continue
            if k == 'fields':
                cls._upsert_template(item.index, v)
            if k == 'policy':
                cls._upsert_lifecycle(item.index, **v)
            setattr(item, k, v)
            
        session.commit()
        return item

    @classmethod
    def delete_model(cls, session, filter_):
        items = cls.get_items(session=session, filter_=filter).all()
        for item in items:
            cls._delete_index(item.index)
        count = cls.delete_items(session=session, filter_=filter_)
        return count

    @classmethod
    def helper_index(cls, session, scope):
        """根据数据源 或数据模型 搜索时间获取搜索索引
        数据源 模型取并集
        """
        model_ids = scope.get('model_ids')  # [] 全不选 | [xx, bb] 勾选
        source_ids = scope.get('source_ids')

        start_ts, end_ts = scope.get('range')
        item_map = {item.id: item for item in session.query(StatisticDSM).all()}

        model_id_set, source_id_set = set(), set()
        for index_name, item in item_map.items():
            k = index_name.rsplit('-', 1)[0]
            if k in model_ids:
                if start_ts is None and end_ts is None:
                    model_id_set.add(index_name)
                    continue
                if start_ts > item.firsted or end_ts < item.lasted or item.is_writeable:
                    model_id_set.add(index_name)

        for index_name, item in item_map.items():
            for source_id in source_ids:
                if source := item.source.get(source_id):
                    if start_ts is None and end_ts is None:
                        source_id_set.add(index_name)
                        continue
                    if start_ts > source['firsted_at'] or end_ts < source['lasted_at'] or item.is_writeable:
                        source_id_set.add(index_name)

        if model_ids and source_ids:  # 都选求交集
            return model_id_set & source_id_set
        if model_ids:  # 只选模型
            return model_id_set
        return source_id_set
